package com.boss.redis.util.distributedlock.config;

import com.boss.redis.util.distributedlock.constant.RedisConnectionType;
import com.boss.redis.util.distributedlock.properties.RedissonProperties;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.redisson.config.ReadMode;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.time.Duration;
import java.util.*;


@Configuration
@Slf4j
public class DistributedLockAutoConfiguration {
    @Bean
    public Config config(RedissonProperties redissonProperties) {
        Config config = null;
        Preconditions.checkNotNull(redissonProperties);
        Preconditions.checkNotNull(redissonProperties.getAddress(), "spring.redis.address cannot be NULL!");
        Preconditions.checkNotNull(redissonProperties.getType(), "spring.redis..password cannot be NULL");
        Preconditions.checkNotNull(redissonProperties.getDatabase(), "spring.redis.database cannot be NULL");
        String connectionType = redissonProperties.getType();
        if (connectionType.equals(RedisConnectionType.STANDALONE.getConnectionType())) {
            config = new StandaloneRedissonConfigStrategyImpl().createRedissonConfig(redissonProperties);
        } else if (connectionType.equals(RedisConnectionType.SENTINEL.getConnectionType())) {
            config = new SentinelRedissonConfigStrategyImpl().createRedissonConfig(redissonProperties);
        } else if (connectionType.equals(RedisConnectionType.CLUSTER.getConnectionType())) {
            config = new ClusterRedissonConfigStrategyImpl().createRedissonConfig(redissonProperties);
        } else if (connectionType.equals(RedisConnectionType.MASTERSLAVE.getConnectionType())) {
            config = new MasterslaveRedissonConfigStrategyImpl().createRedissonConfig(redissonProperties);
        } else {
            throw new IllegalArgumentException("创建Redisson连接Config失败！当前连接方式:" + connectionType);
        }
        return config;
    }

    @Bean
    @Primary
    public JedisConnectionFactory jedisConnectionFactory(RedissonProperties redissonProperties) {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(redissonProperties.getMaxIdle());
        poolConfig.setMinIdle(redissonProperties.getMinIdle());
        poolConfig.setMaxTotal(redissonProperties.getMaxTotal());
        poolConfig.setMinEvictableIdleTimeMillis(redissonProperties.getMinEvictableIdleTimeMillis());
        poolConfig.setNumTestsPerEvictionRun(redissonProperties.getNumTestsPerEvictionRun());
        poolConfig.setTimeBetweenEvictionRunsMillis(redissonProperties.getTimeBetweenEvictionRunsMillis());
        poolConfig.setTestOnBorrow(redissonProperties.getTestOnBorrow());
        poolConfig.setTestWhileIdle(redissonProperties.getTestWhileIdle());
        // 哨兵
       if(RedisConnectionType.SENTINEL.getConnectionType().equals(redissonProperties.getType())){
            RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration();
            // 设置密码
            sentinelConfig.setPassword(RedisPassword.of(redissonProperties.getPassword()));
            sentinelConfig.setDatabase(redissonProperties.getDatabase());
            sentinelConfig.master(redissonProperties.getMaster()).setSentinels(sentinels(redissonProperties.getAddress()));
            return new JedisConnectionFactory(sentinelConfig,poolConfig);
        }else{
           // 单体
           RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
           redisStandaloneConfiguration.setDatabase(redissonProperties.getDatabase());
           String[] split = redissonProperties.getAddress().split(":");
           redisStandaloneConfiguration.setHostName(split[0]);
           redisStandaloneConfiguration.setPort(Integer.valueOf(split[1]));
           redisStandaloneConfiguration.setPassword(RedisPassword.of(redissonProperties.getPassword()));
           return new JedisConnectionFactory(redisStandaloneConfiguration);
       }
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedissonProperties redissonProperties) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory(redissonProperties));
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(getJackson2JsonRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .disableCachingNullValues();
        // 配置序列化
        RedisCacheConfiguration redisCacheConfiguration =
                cacheConfig
                        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(getJackson2JsonRedisSerializer()))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(getJackson2JsonRedisSerializer()));
        return redisCacheConfiguration;
    }

    /**
     * 获取替换默认序列化Jackson2JsonRedisSerialize
     *
     * @author peter
     * @since 2023年01月30日 16:14:42
     */
    private Jackson2JsonRedisSerializer getJackson2JsonRedisSerializer() {
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        return jackson2JsonRedisSerializer;
    }

    @Bean
    public CacheManager redisCacheManager(RedisConnectionFactory factory) {
        RedisCacheManager manager = RedisCacheManager.builder(factory)
                .cacheDefaults(redisCacheConfiguration())
                .transactionAware()
                .build();
        return manager;
    }



    @Bean
    public Redisson redisson(Config config,RedissonProperties redissonProperties) {
        String connectionType = redissonProperties.getType();
        if (connectionType.equals(RedisConnectionType.SENTINEL.getConnectionType())) {
            String address = redissonProperties.getAddress();
            String[] nodeStr = address.split(",");
            List<String> newNodes = new ArrayList(nodeStr.length);
            Arrays.stream(nodeStr).forEach((index)-> newNodes.add(
                    index.startsWith("redis://")?index:"redis://"+index));

            Integer maxTotal = redissonProperties.getMaxTotal();
            Integer minIdle = redissonProperties.getMinIdle();

            config.useSentinelServers()
                    .addSentinelAddress(newNodes.toArray(new String[0]))
                    .setMasterName(redissonProperties.getMaster())
                    .setReadMode(ReadMode.SLAVE)
                    .setPassword(redissonProperties.getPassword())
                    .setMasterConnectionPoolSize(maxTotal)
                    .setSlaveConnectionPoolSize(maxTotal)
                    .setMasterConnectionMinimumIdleSize(minIdle)
                    .setSlaveConnectionMinimumIdleSize(minIdle);


        }

        Redisson redisson = (Redisson) Redisson.create(config);
        return redisson;
    }

//    @Bean
//    public RedissonClient redissonClient(Config config) {
//        return Redisson.create(config);
//    }

    private Set<RedisNode> sentinels(String address) {
        Set<RedisNode> sentinelNodes = new HashSet<>();
        for (String node : address.split(",")) {
            String[] parts = node.split(":");
            sentinelNodes.add(new RedisNode(parts[0], Integer.parseInt(parts[1])));
        }
        return sentinelNodes;
    }
}
